<!--
    /**
     * 树形下拉选择组件，下拉框展示树形结构，提供选择某节点功能，方便其他模块调用
     * @author sxy https://bysjb.cn
     * @date 2020-12-02
     */
-->
<template>
    <div>
      <div
        v-show="isShowSelect"
        class="mask"
        @click="isShowSelect = !isShowSelect"
      />
      <el-popover
        v-model="isShowSelect"
        placement="bottom-start"
        :width="width"
        trigger="manual"
        style="padding: 12px 0"
        @hide="popoverHide"
      >
        <el-tree
          ref="tree"
          class="common-tree"
          :style="style"
          :data="data"
          :props="defaultProps"
          :show-checkbox="multiple"
          :node-key="nodeKey"
          :check-strictly="checkStrictly"
          default-expand-all
          :expand-on-click-node="false"
          :check-on-click-node="multiple"
          :highlight-current="true"
          @node-click="handleNodeClick"
          @check-change="handleCheckChange"
        />
        <el-select
          slot="reference"
          ref="select"
          v-model="selectedData"
          :style="selectStyle"
          :size="size"
          :multiple="multiple"
          :clearable="clearable"
          :collapse-tags="collapseTags"
          class="tree-select"
          @click.native="isShowSelect = !isShowSelect"
          @remove-tag="removeSelectedNodes"
          @clear="removeSelectedNode"
          @change="changeSelectedNodes"
        >
          <el-option
            v-for="item in options"
            :key="item.value"
            :label="item.label"
            :value="item.value"
          />
        </el-select>
      </el-popover>
    </div>
  </template>
  
  <script>
  export default {
    name: "common-tree",
    props: {
      // 树结构数据
      data: {
        type: Array,
        default() {
          return [];
        },
      },
      defaultProps: {
        type: Object,
        default() {
          return {
            children: "children",
            label: "name",
          };
        },
      },
      // 配置是否可多选
      multiple: {
        type: Boolean,
        default() {
          return false;
        },
      },
      // 配置是否可清空选择
      clearable: {
        type: Boolean,
        default() {
          return false;
        },
      },
      // 配置多选时是否将选中值按文字的形式展示
      collapseTags: {
        type: Boolean,
        default() {
          return false;
        },
      },
      nodeKey: {
        type: String,
        default() {
          return "id";
        },
      },
      // 显示复选框情况下，是否严格遵循父子不互相关联
      checkStrictly: {
        type: Boolean,
        default() {
          return false;
        },
      },
      // 默认选中的节点key数组
      checkedKeys: {
        type: Array,
        default() {
          return [];
        },
      },
      size: {
        type: String,
        default() {
          return "medium";
        },
      },
      width: {
        type: Number,
        default() {
          return 250;
        },
      },
      height: {
        type: Number,
        default() {
          return 300;
        },
      },
    },
    data() {
      return {
        isShowSelect: false, // 是否显示树状选择器
        options: [],
        selectedData: [], // 选中的节点
        style:
          "width:" + (this.width - 24) + "px;" + "height:" + this.height + "px;",
        selectStyle: "width:" + (this.width + 24) + "px;",
        checkedIds: [],
        checkedData: [],
      };
    },
    watch: {
      isShowSelect(val) {
          console.log(val)
        // 隐藏select自带的下拉框
        this.$refs.select.blur();
      },
      checkedKeys(val) {
        console.log("checkedKeys", val);
        if (!val) return;
        this.checkedKeys = val;
        this.initCheckedData();
      },
    },
    mounted() {
      this.initCheckedData();
    },
    methods: {
      // 单选时点击tree节点，设置select选项
      setSelectOption(node) {
        const tmpMap = {};
        tmpMap.value = node.key;
        tmpMap.label = node.label;
        this.options = [];
        this.options.push(tmpMap);
        this.selectedData = node.key;
      },
      // 单选，选中传进来的节点
      checkSelectedNode(checkedKeys) {
        var item = checkedKeys[0];
        this.$refs.tree.setCurrentKey(item);
        var node = this.$refs.tree.getNode(item);
        this.setSelectOption(node);
      },
      // 多选，勾选上传进来的节点
      checkSelectedNodes(checkedKeys) {
        // this.$refs.tree.setCheckedKeys(checkedKeys)
  
        // 优化select回显显示 有个延迟的效果
        const that = this;
        setTimeout(function () {
          that.$refs.tree.setCheckedKeys(checkedKeys);
        }, 10);
        // console.log('checkSelectedNodes', checkedKeys, this.selectedData)
      },
      // 单选，清空选中
      clearSelectedNode() {
        this.selectedData = [];
        this.$refs.tree.setCurrentKey(null);
      },
      // 多选，清空所有勾选
      clearSelectedNodes() {
        var checkedKeys = this.$refs.tree.getCheckedKeys(); // 所有被选中的节点的 key 所组成的数组数据
        for (let i = 0; i < checkedKeys.length; i++) {
          this.$refs.tree.setChecked(checkedKeys[i], false);
        }
      },
      initCheckedData() {
        if (this.multiple) {
          // 多选
          // console.log(this.checkedKeys.length)
          if (this.checkedKeys.length > 0) {
            this.checkSelectedNodes(this.checkedKeys);
          } else {
            this.clearSelectedNodes();
          }
        } else {
          // 单选
          if (this.checkedKeys.length > 0) {
            this.checkSelectedNode(this.checkedKeys);
          } else {
            this.clearSelectedNode();
          }
        }
      },
      popoverHide() {
        if (this.multiple) {
          this.checkedIds = this.$refs.tree.getCheckedKeys(); // 所有被选中的节点的 key 所组成的数组数据
          this.checkedData = this.$refs.tree.getCheckedNodes(); // 所有被选中的节点所组成的数组数据
        } else {
          this.checkedIds = this.$refs.tree.getCurrentKey();
          this.checkedData = this.$refs.tree.getCurrentNode();
        }
        this.$emit("popoverHide", this.checkedIds, this.checkedData);
      },
      // 单选，节点被点击时的回调,返回被点击的节点数据
      handleNodeClick(data, node) {
        if (!this.multiple) {
          this.setSelectOption(node);
          this.isShowSelect = !this.isShowSelect;
          this.$emit("change", this.selectedData);
        }
      },
      // 多选，节点勾选状态发生变化时的回调
      handleCheckChange() {
        var checkedKeys = this.$refs.tree.getCheckedKeys(); // 所有被选中的节点的 key 所组成的数组数据
        // console.log('handleCheckChange', checkedKeys)
  
        this.options = checkedKeys.map((item) => {
          var node = this.$refs.tree.getNode(item); // 所有被选中的节点对应的node
          const tmpMap = {};
          tmpMap.value = node.key;
          tmpMap.label = node.label;
          return tmpMap;
        });
        this.selectedData = this.options.map((item) => {
          return item.value;
        });
        this.$emit("change", this.selectedData);
      },
      // 多选,删除任一select选项的回调
      removeSelectedNodes(val) {
        this.$refs.tree.setChecked(val, false);
        var node = this.$refs.tree.getNode(val);
        if (!this.checkStrictly && node.childNodes.length > 0) {
          this.treeToList(node).map((item) => {
            if (item.childNodes.length <= 0) {
              this.$refs.tree.setChecked(item, false);
            }
          });
          this.handleCheckChange();
        }
        this.$emit("change", this.selectedData);
      },
      treeToList(tree) {
        var queen = [];
        var out = [];
        queen = queen.concat(tree);
        while (queen.length) {
          var first = queen.shift();
          if (first.childNodes) {
            queen = queen.concat(first.childNodes);
          }
          out.push(first);
        }
        return out;
      },
      // 单选,清空select输入框的回调
      removeSelectedNode() {
        this.clearSelectedNode();
        this.$emit("change", this.selectedData);
      },
      // 选中的select选项改变的回调
      changeSelectedNodes(selectedData) {
        // 多选,清空select输入框时，清除树勾选
        if (this.multiple && selectedData.length <= 0) {
          this.clearSelectedNodes();
        }
        this.$emit("change", this.selectedData);
      },
    },
  };
  </script>
  
  <style scoped>
  .mask {
    width: 100%;
    height: 100%;
    position: fixed;
    top: 0;
    left: 0;
    opacity: 0;
    z-index: 11;
  }
  
  .common-tree {
    overflow: auto;
  }
  
  .tree-select {
    z-index: 111;
  }
  </style>
  